package com.swallow.auth.infrastructure.acl.oauth2.support.username;

import com.swallow.auth.infrastructure.acl.oauth2.constants.SecurityConstants;
import com.swallow.auth.infrastructure.acl.oauth2.utils.SecurityUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author: yangjie.deng@resico.cn
 * @since: 2024-05-15 10:43:59
 * @version: v1.0.0
 * @describe: 自定义用户名密码登录转换器
 */
public class UPwdGrantAuthenticationConverter implements AuthenticationConverter {
    @Override
    public Authentication convert(HttpServletRequest request) {
        String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);

        if (!StringUtils.equals(SecurityConstants.GRANT_TYPE_USER_PWD_CODE, grantType)) {
            return null;
        }
        // 提取请求参数
        MultiValueMap<String, String> parameters = SecurityUtils.getParameters(request);
        // scope 获取
        var scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
        if (StringUtils.isNotBlank(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
            SecurityUtils.throwError( OAuth2ErrorCodes.INVALID_REQUEST, "用户名密码登录: 授权范围不正确");
        }

        Set<String> requestedScopes = null;
        if (StringUtils.isNotBlank(scope)) {
            requestedScopes = Stream.of(scope.split(",")).collect(Collectors.toSet());
        }

        var username = parameters.getFirst(SecurityConstants.U_PWD_USERNAME_PARAM);
        if (StringUtils.isBlank(username) || parameters.get(SecurityConstants.U_PWD_USERNAME_PARAM).size() != 1) {
            SecurityUtils.throwError( OAuth2ErrorCodes.INVALID_REQUEST, "用户名密码登录: 用户名不能为空");
        }

        var password = parameters.getFirst(SecurityConstants.U_PWD_PASSWORD_PARAM);
        if (StringUtils.isBlank(password) || parameters.get(SecurityConstants.U_PWD_PASSWORD_PARAM).size() != 1) {
            SecurityUtils.throwError( OAuth2ErrorCodes.INVALID_REQUEST, "用户名密码登录: 密码不能为空");
        }

        var captchaId = parameters.getFirst(SecurityConstants.U_PWD_CAPTCHA_ID_PARAM);
        if (StringUtils.isBlank(captchaId) || parameters.get(SecurityConstants.U_PWD_CAPTCHA_ID_PARAM).size() != 1) {
            SecurityUtils.throwError( OAuth2ErrorCodes.INVALID_REQUEST, "用户名密码登录: 图形验证码参数错误");
        }

        var captchaCode = parameters.getFirst(SecurityConstants.U_PWD_CAPTCHA_PARAM);
        if (StringUtils.isBlank(captchaCode) || parameters.get(SecurityConstants.U_PWD_CAPTCHA_PARAM).size() != 1) {
            SecurityUtils.throwError( OAuth2ErrorCodes.INVALID_REQUEST, "用户名密码登录: 图形验证码错误");
        }
        // 提取附加参数
        Map<String, Object> additionalParameters = new HashMap<>();

        parameters.entrySet()
                  .stream()
                  .filter(entry -> !StringUtils.equalsAny(entry.getKey(), OAuth2ParameterNames.GRANT_TYPE,OAuth2ParameterNames.CLIENT_ID))
                  .forEach(entry -> additionalParameters.put(entry.getKey(), entry.getValue().getFirst()));

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        return new UPwdGrantAuthenticationToken(authentication.getAuthorities(),
                                                requestedScopes,
                                                authentication,
                                                additionalParameters,
                                                new AuthorizationGrantType(SecurityConstants.GRANT_TYPE_USER_PWD_CODE)
                                               );
    }
}
